﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace BrasilFramework.Controle.Silverlight
{
    /// <summary>
    /// Represents a Circular Gauge control
    /// </summary>
    [TemplatePart(Name = "LayoutRoot", Type = typeof (Grid))]
    [TemplatePart(Name = "Pointer", Type = typeof (Path))]
    [TemplatePart(Name = "RangeIndicatorLight", Type = typeof (Ellipse))]
    [TemplatePart(Name = "PointerCap", Type = typeof (Ellipse))]
    public class CircularGauge : Control
    {
        #region Private variables

        //Private variables
        private const int animatingSpeedFactor = 6;
        private Double arcradius1;
        private Double arcradius2;
        private bool isInitialValueSet;
        private Ellipse lightIndicator;
        private Path pointer;
        private Ellipse pointerCap;
        private Path rangeIndicator;
        private Grid rootGrid;

        #endregion

        #region Dependency properties

        /// <summary>
        /// Dependency property to Get/Set the current value 
        /// </summary>
        public static readonly DependencyProperty CurrentValueProperty = DependencyProperty.Register("CurrentValue", typeof (double), typeof (CircularGauge), new PropertyMetadata(Double.MinValue, OnCurrentValuePropertyChanged));

        /// <summary>
        /// Dependency property to Get/Set the Minimum Value 
        /// </summary>
        public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Maximum Value 
        /// </summary>
        public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Radius of the gauge
        /// </summary>
        public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Pointer cap Radius
        /// </summary>
        public static readonly DependencyProperty PointerCapRadiusProperty = DependencyProperty.Register("PointerCapRadius", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the pointer length
        /// </summary>
        public static readonly DependencyProperty PointerLengthProperty = DependencyProperty.Register("PointerLength", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the scale Radius
        /// </summary>
        public static readonly DependencyProperty ScaleRadiusProperty = DependencyProperty.Register("ScaleRadius", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the starting angle of scale
        /// </summary>
        public static readonly DependencyProperty ScaleStartAngleProperty = DependencyProperty.Register("ScaleStartAngle", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the sweep angle of scale
        /// </summary>
        public static readonly DependencyProperty ScaleSweepAngleProperty = DependencyProperty.Register("ScaleSweepAngle", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the number of major divisions on the scale
        /// </summary>
        public static readonly DependencyProperty MajorDivisionsCountProperty = DependencyProperty.Register("MajorDivisionsCount", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the number of minor divisions on the scale
        /// </summary>
        public static readonly DependencyProperty MinorDivisionsCountProperty = DependencyProperty.Register("MinorDivisionsCount", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set Optimal Range End Value
        /// </summary>
        public static readonly DependencyProperty OptimalRangeEndValueProperty = DependencyProperty.Register("OptimalRangeEndValue", typeof (double), typeof (CircularGauge), new PropertyMetadata(new PropertyChangedCallback(OnOptimalRangeEndValuePropertyChanged)));

        /// <summary>
        /// Dependency property to Get/Set Optimal Range Start Value
        /// </summary>
        public static readonly DependencyProperty OptimalRangeStartValueProperty = DependencyProperty.Register("OptimalRangeStartValue", typeof (double), typeof (CircularGauge), new PropertyMetadata(new PropertyChangedCallback(OnOptimalRangeStartValuePropertyChanged)));

        /// <summary>
        /// Dependency property to Get/Set the image source
        /// </summary>
        public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof (ImageSource), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the image offset
        /// </summary>
        public static readonly DependencyProperty ImageOffsetProperty = DependencyProperty.Register("ImageOffset", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the range indicator light offset
        /// </summary>
        public static readonly DependencyProperty RangeIndicatorLightOffsetProperty = DependencyProperty.Register("RangeIndicatorLightOffset", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the image Size
        /// </summary>
        public static readonly DependencyProperty ImageSizeProperty = DependencyProperty.Register("ImageSize", typeof (Size), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Range Indicator Radius
        /// </summary>
        public static readonly DependencyProperty RangeIndicatorRadiusProperty = DependencyProperty.Register("RangeIndicatorRadius", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Range Indicator Thickness
        /// </summary>
        public static readonly DependencyProperty RangeIndicatorThicknessProperty = DependencyProperty.Register("RangeIndicatorThickness", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the scale label Radius
        /// </summary>
        public static readonly DependencyProperty ScaleLabelRadiusProperty = DependencyProperty.Register("ScaleLabelRadius", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Scale Label Size
        /// </summary>
        public static readonly DependencyProperty ScaleLabelSizeProperty = DependencyProperty.Register("ScaleLabelSize", typeof (Size), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Scale Label FontSize
        /// </summary>
        public static readonly DependencyProperty ScaleLabelFontSizeProperty = DependencyProperty.Register("ScaleLabelFontSize", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Scale Label Foreground
        /// </summary>
        public static readonly DependencyProperty ScaleLabelForegroundProperty = DependencyProperty.Register("ScaleLabelForeground", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Major Tick Size
        /// </summary>
        public static readonly DependencyProperty MajorTickSizeProperty = DependencyProperty.Register("MajorTickRectSize", typeof (Size), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Minor Tick Size
        /// </summary>
        public static readonly DependencyProperty MinorTickSizeProperty = DependencyProperty.Register("MinorTickSize", typeof (Size), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Major Tick Color
        /// </summary>
        public static readonly DependencyProperty MajorTickColorProperty = DependencyProperty.Register("MajorTickColor", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Minor Tick Color
        /// </summary>
        public static readonly DependencyProperty MinorTickColorProperty = DependencyProperty.Register("MinorTickColor", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Gauge Background Color
        /// </summary>
        public static readonly DependencyProperty GaugeBackgroundColorProperty = DependencyProperty.Register("GaugeBackgroundColor", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Pointer Thickness
        /// </summary>
        public static readonly DependencyProperty PointerThicknessProperty = DependencyProperty.Register("PointerThickness", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the an option to reset the pointer on start up to the minimum value
        /// </summary>
        public static readonly DependencyProperty ResetPointerOnStartUpProperty = DependencyProperty.Register("ResetPointerOnStartUp", typeof (bool), typeof (CircularGauge), new PropertyMetadata(false, null));

        /// <summary>
        /// Dependency property to Get/Set the Scale Value Precision
        /// </summary>
        public static readonly DependencyProperty ScaleValuePrecisionProperty = DependencyProperty.Register("ScaleValuePrecision", typeof (int), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Below Optimal Range Color
        /// </summary>
        public static readonly DependencyProperty BelowOptimalRangeColorProperty = DependencyProperty.Register("BelowOptimalRangeColor", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Optimal Range Color
        /// </summary>
        public static readonly DependencyProperty OptimalRangeColorProperty = DependencyProperty.Register("OptimalRangeColor", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Above Optimal Range Color
        /// </summary>
        public static readonly DependencyProperty AboveOptimalRangeColorProperty = DependencyProperty.Register("AboveOptimalRangeColor", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Dial Text
        /// </summary>
        public static readonly DependencyProperty DialTextProperty = DependencyProperty.Register("DialText", typeof (string), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Dial Text Color
        /// </summary>
        public static readonly DependencyProperty DialTextColorProperty = DependencyProperty.Register("DialTextColor", typeof (Color), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Dial Text Font Size
        /// </summary>
        public static readonly DependencyProperty DialTextFontSizeProperty = DependencyProperty.Register("DialTextFontSize", typeof (int), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Dial Text Offset
        /// </summary>
        public static readonly DependencyProperty DialTextOffsetProperty = DependencyProperty.Register("DialTextOffset", typeof (double), typeof (CircularGauge), null);

        /// <summary>
        /// Dependency property to Get/Set the Range Indicator light Radius
        /// </summary>
        public static readonly DependencyProperty RangeIndicatorLightRadiusProperty = DependencyProperty.Register("RangeIndicatorLightRadius", typeof (double), typeof (CircularGauge), null);

        #endregion

        #region Wrapper properties

        /// <summary>
        /// Gets/Sets the current value
        /// </summary>
        public double CurrentValue
        {
            get { return (double) GetValue(CurrentValueProperty); }
            set { SetValue(CurrentValueProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Minimum Value
        /// </summary>
        public double MinValue
        {
            get { return (double) GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Maximum Value
        /// </summary>
        public double MaxValue
        {
            get { return (double) GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Minimum Value
        /// </summary>
        public double Radius
        {
            get { return (double) GetValue(RadiusProperty); }
            set { SetValue(RadiusProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Pointer cap radius
        /// </summary>
        public double PointerCapRadius
        {
            get { return (double) GetValue(PointerCapRadiusProperty); }
            set { SetValue(PointerCapRadiusProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Pointer Length
        /// </summary>
        public double PointerLength
        {
            get { return (double) GetValue(PointerLengthProperty); }
            set { SetValue(PointerLengthProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Pointer Thickness
        /// </summary>
        public double PointerThickness
        {
            get { return (double) GetValue(PointerThicknessProperty); }
            set { SetValue(PointerThicknessProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Scale radius
        /// </summary>
        public double ScaleRadius
        {
            get { return (double) GetValue(ScaleRadiusProperty); }
            set { SetValue(ScaleRadiusProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the scale start angle
        /// </summary>
        public double ScaleStartAngle
        {
            get { return (double) GetValue(ScaleStartAngleProperty); }
            set { SetValue(ScaleStartAngleProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the scale sweep angle
        /// </summary>
        public double ScaleSweepAngle
        {
            get { return (double) GetValue(ScaleSweepAngleProperty); }
            set { SetValue(ScaleSweepAngleProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the number of major divisions on the scale
        /// </summary>
        public double MajorDivisionsCount
        {
            get { return (double) GetValue(MajorDivisionsCountProperty); }
            set { SetValue(MajorDivisionsCountProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the number of minor divisions on the scale
        /// </summary>
        public double MinorDivisionsCount
        {
            get { return (double) GetValue(MinorDivisionsCountProperty); }
            set { SetValue(MinorDivisionsCountProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Optimal range end value
        /// </summary>
        public double OptimalRangeEndValue
        {
            get { return (double) GetValue(OptimalRangeEndValueProperty); }
            set { SetValue(OptimalRangeEndValueProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Optimal Range Start Value
        /// </summary>
        public double OptimalRangeStartValue
        {
            get { return (double) GetValue(OptimalRangeStartValueProperty); }
            set { SetValue(OptimalRangeStartValueProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Gauge image source
        /// </summary>
        public ImageSource ImageSource
        {
            get { return (ImageSource) GetValue(ImageSourceProperty); }
            set { SetValue(ImageSourceProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Image offset
        /// </summary>
        public double ImageOffset
        {
            get { return (double) GetValue(ImageOffsetProperty); }
            set { SetValue(ImageOffsetProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Range Indicator Light offset
        /// </summary>
        public double RangeIndicatorLightOffset
        {
            get { return (double) GetValue(RangeIndicatorLightOffsetProperty); }
            set { SetValue(RangeIndicatorLightOffsetProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Image width and height
        /// </summary>
        public Size ImageSize
        {
            get { return (Size) GetValue(ImageSizeProperty); }
            set { SetValue(ImageSizeProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Range Indicator Radius 
        /// </summary>
        public double RangeIndicatorRadius
        {
            get { return (double) GetValue(RangeIndicatorRadiusProperty); }
            set { SetValue(RangeIndicatorRadiusProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Range Indicator Thickness 
        /// </summary>
        public double RangeIndicatorThickness
        {
            get { return (double) GetValue(RangeIndicatorThicknessProperty); }
            set { SetValue(RangeIndicatorThicknessProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Scale Label Radius 
        /// </summary>
        public double ScaleLabelRadius
        {
            get { return (double) GetValue(ScaleLabelRadiusProperty); }
            set { SetValue(ScaleLabelRadiusProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Scale Label Size 
        /// </summary>
        public Size ScaleLabelSize
        {
            get { return (Size) GetValue(ScaleLabelSizeProperty); }
            set { SetValue(ScaleLabelSizeProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Scale Label Font Size 
        /// </summary>
        public double ScaleLabelFontSize
        {
            get { return (double) GetValue(ScaleLabelFontSizeProperty); }
            set { SetValue(ScaleLabelFontSizeProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Scale Label Foreground 
        /// </summary>
        public Color ScaleLabelForeground
        {
            get { return (Color) GetValue(ScaleLabelForegroundProperty); }
            set { SetValue(ScaleLabelForegroundProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Major Tick Size 
        /// </summary>
        public Size MajorTickSize
        {
            get { return (Size) GetValue(MajorTickSizeProperty); }
            set { SetValue(MajorTickSizeProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Minor Tick Size 
        /// </summary>
        public Size MinorTickSize
        {
            get { return (Size) GetValue(MinorTickSizeProperty); }
            set { SetValue(MinorTickSizeProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Major Tick Color 
        /// </summary>
        public Color MajorTickColor
        {
            get { return (Color) GetValue(MajorTickColorProperty); }
            set { SetValue(MajorTickColorProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Minor Tick Color 
        /// </summary>
        public Color MinorTickColor
        {
            get { return (Color) GetValue(MinorTickColorProperty); }
            set { SetValue(MinorTickColorProperty, value); }
        }

        /// <summary>
        /// Gets/Sets the Gauge Background color
        /// </summary>
        public Color GaugeBackgroundColor
        {
            get { return (Color) GetValue(GaugeBackgroundColorProperty); }
            set { SetValue(GaugeBackgroundColorProperty, value); }
        }

        /// <summary>
        /// Gets/Sets option to reset the pointer to minimum on start up, Default is true
        /// </summary>
        public bool ResetPointerOnStartUp
        {
            get { return (bool) GetValue(ResetPointerOnStartUpProperty); }
            set { SetValue(ResetPointerOnStartUpProperty, value); }
        }

        /// <summary>
        /// Gets/Sets scale value precision 
        /// </summary>
        public int ScaleValuePrecision
        {
            get { return (int) GetValue(ScaleValuePrecisionProperty); }
            set { SetValue(ScaleValuePrecisionProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Below Optimal Range Color
        /// </summary>
        public Color BelowOptimalRangeColor
        {
            get { return (Color) GetValue(BelowOptimalRangeColorProperty); }
            set { SetValue(BelowOptimalRangeColorProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Optimal Range Color
        /// </summary>
        public Color OptimalRangeColor
        {
            get { return (Color) GetValue(OptimalRangeColorProperty); }
            set { SetValue(OptimalRangeColorProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Above Optimal Range Color
        /// </summary>
        public Color AboveOptimalRangeColor
        {
            get { return (Color) GetValue(AboveOptimalRangeColorProperty); }
            set { SetValue(AboveOptimalRangeColorProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Dial Text
        /// </summary>
        public string DialText
        {
            get { return (string) GetValue(DialTextProperty); }
            set { SetValue(DialTextProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Dial Text Color
        /// </summary>
        public Color DialTextColor
        {
            get { return (Color) GetValue(DialTextColorProperty); }
            set { SetValue(DialTextColorProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Dial Text Font Size
        /// </summary>
        public int DialTextFontSize
        {
            get { return (int) GetValue(DialTextFontSizeProperty); }
            set { SetValue(DialTextFontSizeProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Dial Text Offset
        /// </summary>
        public double DialTextOffset
        {
            get { return (double) GetValue(DialTextOffsetProperty); }
            set { SetValue(DialTextOffsetProperty, value); }
        }

        /// <summary>
        /// Gets/Sets Range Indicator Light Radius
        /// </summary>
        public double RangeIndicatorLightRadius
        {
            get { return (double) GetValue(RangeIndicatorLightRadiusProperty); }
            set { SetValue(RangeIndicatorLightRadiusProperty, value); }
        }

        #endregion

        #region Constructor

        public CircularGauge()
        {
            //Set default style key
            DefaultStyleKey = typeof (CircularGauge);
        }

        #endregion

        #region Methods

        private static void OnCurrentValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Get access to the instance of CircularGaugeConrol whose property value changed
            var gauge = d as CircularGauge;
            if (gauge != null) gauge.OnCurrentValueChanged(e);
        }

        private static void OnOptimalRangeEndValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Get access to the instance of CircularGaugeConrol whose property value changed
            var gauge = d as CircularGauge;
            if (gauge != null)
                if ((double) e.NewValue > gauge.MaxValue)
                {
                    gauge.OptimalRangeEndValue = gauge.MaxValue;
                }
        }

        private static void OnOptimalRangeStartValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Get access to the instance of CircularGaugeConrol whose property value changed
            var gauge = d as CircularGauge;
            if (gauge != null)
                if ((double) e.NewValue < gauge.MinValue)
                {
                    gauge.OptimalRangeStartValue = gauge.MinValue;
                }
        }

        public virtual void OnCurrentValueChanged(DependencyPropertyChangedEventArgs e)
        {
            //Validate and set the new value
            var newValue = (double) e.NewValue;
            var oldValue = (double) e.OldValue;

            if (newValue > MaxValue)
            {
                newValue = MaxValue;
            }
            else if (newValue < MinValue)
            {
                newValue = MinValue;
            }

            if (oldValue > MaxValue)
            {
                oldValue = MaxValue;
            }
            else if (oldValue < MinValue)
            {
                oldValue = MinValue;
            }

            if (pointer == null) return;

            double db1;
            Double oldcurr_realworldunit;
            Double newcurr_realworldunit;
            Double realworldunit = (ScaleSweepAngle/(MaxValue - MinValue));
            //Resetting the old value to min value the very first time.
            if (oldValue == 0 && !isInitialValueSet)
            {
                oldValue = MinValue;
                isInitialValueSet = true;
            }
            if (oldValue < 0)
            {
                db1 = MinValue + Math.Abs(oldValue);
                oldcurr_realworldunit = ((Math.Abs(db1*realworldunit)));
            }
            else
            {
                db1 = Math.Abs(MinValue) + oldValue;
                oldcurr_realworldunit = ((db1*realworldunit));
            }
            if (newValue < 0)
            {
                db1 = MinValue + Math.Abs(newValue);
                newcurr_realworldunit = ((Math.Abs(db1*realworldunit)));
            }
            else
            {
                db1 = Math.Abs(MinValue) + newValue;
                newcurr_realworldunit = ((db1*realworldunit));
            }

            Double oldcurrentvalueAngle = (ScaleStartAngle + oldcurr_realworldunit);
            Double newcurrentvalueAngle = (ScaleStartAngle + newcurr_realworldunit);

            //Animate the pointer from the old value to the new value
            AnimatePointer(oldcurrentvalueAngle, newcurrentvalueAngle);
        }

        /// <summary>
        /// Animates the pointer to the current value to the new one
        /// </summary>
        /// <param name="oldcurrentvalueAngle"></param>
        /// <param name="newcurrentvalueAngle"></param>
        private void AnimatePointer(double oldcurrentvalueAngle, double newcurrentvalueAngle)
        {
            if (pointer == null) return;

            var da = new DoubleAnimation {From = oldcurrentvalueAngle, To = newcurrentvalueAngle};

            double animDuration = Math.Abs(oldcurrentvalueAngle - newcurrentvalueAngle)*animatingSpeedFactor;
            da.Duration = new Duration(TimeSpan.FromMilliseconds(animDuration));

            var sb = new Storyboard();
            sb.Completed += sb_Completed;
            sb.Children.Add(da);
            Storyboard.SetTarget(da, pointer);
            Storyboard.SetTargetProperty(da, new PropertyPath("(Path.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));

            if (newcurrentvalueAngle != oldcurrentvalueAngle)
            {
                sb.Begin();
            }
        }

        /// <summary>
        /// Move pointer without animating
        /// </summary>
        /// <param name="angleValue"></param>
        private void MovePointer(double angleValue)
        {
            if (pointer == null) return;
            var tg = pointer.RenderTransform as TransformGroup;
            if (tg == null) return;
            var rt = tg.Children[0] as RotateTransform;
            if (rt != null) rt.Angle = angleValue;
        }

        /// <summary>
        /// Switch on the Range indicator light after the pointer completes animating
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void sb_Completed(object sender, EventArgs e)
        {
            if (CurrentValue > OptimalRangeEndValue)
            {
                lightIndicator.Fill = GetRangeIndicatorGradEffect(AboveOptimalRangeColor);
            }
            else if (CurrentValue <= OptimalRangeEndValue && CurrentValue >= OptimalRangeStartValue)
            {
                lightIndicator.Fill = GetRangeIndicatorGradEffect(OptimalRangeColor);
            }
            else if (CurrentValue < OptimalRangeStartValue)
            {
                lightIndicator.Fill = GetRangeIndicatorGradEffect(BelowOptimalRangeColor);
            }
        }

        /// <summary>
        /// Get gradient brush effect for the range indicator light
        /// </summary>
        /// <param name="gradientColor"></param>
        /// <returns></returns>
        private static GradientBrush GetRangeIndicatorGradEffect(Color gradientColor)
        {
            var gradient = new LinearGradientBrush {StartPoint = new Point(0, 0), EndPoint = new Point(1, 1)};
            var color1 = new GradientStop
                             {
                                 Color = gradientColor == Colors.Transparent ? gradientColor : Colors.LightGray,
                                 Offset = 0.2
                             };

            gradient.GradientStops.Add(color1);
            var color2 = new GradientStop {Color = gradientColor, Offset = 0.5};
            gradient.GradientStops.Add(color2);
            var color3 = new GradientStop {Color = gradientColor, Offset = 0.8};
            gradient.GradientStops.Add(color3);
            return gradient;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            //Get reference to known elements on the control template
            rootGrid = GetTemplateChild("LayoutRoot") as Grid;
            pointer = GetTemplateChild("Pointer") as Path;
            pointerCap = GetTemplateChild("PointerCap") as Ellipse;
            lightIndicator = GetTemplateChild("RangeIndicatorLight") as Ellipse;

            //Draw scale and range indicator
            DrawScale();
            DrawRangeIndicator();

            //Set Zindex of pointer and pointer cap to a really high number so that it stays on top of the 
            //scale and the range indicator
            if (pointer != null) Canvas.SetZIndex(pointer, 100000);
            if (pointerCap != null) Canvas.SetZIndex(pointerCap, 100001);

            if (ResetPointerOnStartUp)
            {
                //Reset Pointer
                MovePointer(ScaleStartAngle);
            }
        }

        //Drawing the scale with the Scale Radius
        private void DrawScale()
        {
            //Calculate one major tick angle 
            Double majorTickUnitAngle = ScaleSweepAngle/MajorDivisionsCount;

            //Obtaining One minor tick angle 
            Double minorTickUnitAngle = ScaleSweepAngle/MinorDivisionsCount;

            //Obtaining One major ticks value
            Double majorTicksUnitValue = (MaxValue - MinValue)/MajorDivisionsCount;
            majorTicksUnitValue = Math.Round(majorTicksUnitValue, ScaleValuePrecision);

            Double minvalue = MinValue;

            // Drawing Major scale ticks
            for (Double i = ScaleStartAngle; i <= (ScaleStartAngle + ScaleSweepAngle); i = i + majorTickUnitAngle)
            {
                //Majortick is drawn as a rectangle 
                var majortickrect = new Rectangle
                                        {
                                            Height = MajorTickSize.Height,
                                            Width = MajorTickSize.Width,
                                            Fill = new SolidColorBrush(MajorTickColor)
                                        };
                var p = new Point(0.5, 0.5);
                majortickrect.RenderTransformOrigin = p;
                majortickrect.HorizontalAlignment = HorizontalAlignment.Center;
                majortickrect.VerticalAlignment = VerticalAlignment.Center;

                var majortickgp = new TransformGroup();
                var majortickrt = new RotateTransform();

                //Obtaining the angle in radians for calulating the points
                Double i_radian = (i*Math.PI)/180;
                majortickrt.Angle = i;
                majortickgp.Children.Add(majortickrt);
                var majorticktt = new TranslateTransform
                                      {
                                          X = (int) ((ScaleRadius)*Math.Cos(i_radian)),
                                          Y = (int) ((ScaleRadius)*Math.Sin(i_radian))
                                      };

                //Finding the point on the Scale where the major ticks are drawn
                //here drawing the points with center as (0,0)

                //Points for the textblock which hold the scale value
                //here drawing the points with center as (0,0)
                var majorscalevaluett = new TranslateTransform
                                            {
                                                X = (int) ((ScaleLabelRadius)*Math.Cos(i_radian)),
                                                Y = (int) ((ScaleLabelRadius)*Math.Sin(i_radian))
                                            };

                //Defining the properties of the scale value textbox
                var tb = new TextBlock
                             {
                                 Height = ScaleLabelSize.Height,
                                 Width = ScaleLabelSize.Width,
                                 FontSize = ScaleLabelFontSize,
                                 Foreground = new SolidColorBrush(ScaleLabelForeground),
                                 TextAlignment = TextAlignment.Center,
                                 VerticalAlignment = VerticalAlignment.Center,
                                 HorizontalAlignment = HorizontalAlignment.Center
                             };

                //Writing and appending the scale value

                //checking minvalue < maxvalue w.r.t scale precion value
                if (Math.Round(minvalue, ScaleValuePrecision) <= Math.Round(MaxValue, ScaleValuePrecision))
                {
                    minvalue = Math.Round(minvalue, ScaleValuePrecision);
                    tb.Text = minvalue.ToString();
                    minvalue = minvalue + majorTicksUnitValue;
                }
                else
                {
                    break;
                }
                majortickgp.Children.Add(majorticktt);
                majortickrect.RenderTransform = majortickgp;
                tb.RenderTransform = majorscalevaluett;
                rootGrid.Children.Add(majortickrect);
                rootGrid.Children.Add(tb);

                //Drawing the minor axis ticks
                Double onedegree = ((i + majorTickUnitAngle) - i)/(MinorDivisionsCount);

                if ((i < (ScaleStartAngle + ScaleSweepAngle)) && (Math.Round(minvalue, ScaleValuePrecision) <= Math.Round(MaxValue, ScaleValuePrecision)))
                {
                    //Drawing the minor scale
                    for (Double mi = i + onedegree; mi < (i + majorTickUnitAngle); mi = mi + onedegree)
                    {
                        //here the minortick is drawn as a rectangle 
                        var mr = new Rectangle
                                     {
                                         Height = MinorTickSize.Height,
                                         Width = MinorTickSize.Width,
                                         Fill = new SolidColorBrush(MinorTickColor),
                                         HorizontalAlignment = HorizontalAlignment.Center,
                                         VerticalAlignment = VerticalAlignment.Center
                                     };
                        var p1 = new Point(0.5, 0.5);
                        mr.RenderTransformOrigin = p1;

                        var minortickgp = new TransformGroup();
                        var minortickrt = new RotateTransform {Angle = mi};
                        minortickgp.Children.Add(minortickrt);
                        var minorticktt = new TranslateTransform();

                        //Obtaining the angle in radians for calulating the points
                        Double mi_radian = (mi*Math.PI)/180;
                        //Finding the point on the Scale where the minor ticks are drawn
                        minorticktt.X = (int) ((ScaleRadius)*Math.Cos(mi_radian));
                        minorticktt.Y = (int) ((ScaleRadius)*Math.Sin(mi_radian));

                        minortickgp.Children.Add(minorticktt);
                        mr.RenderTransform = minortickgp;
                        rootGrid.Children.Add(mr);
                    }
                }
            }
        }

        /// <summary>
        /// Obtaining the Point (x,y) in the circumference 
        /// </summary>
        /// <param name="angle"></param>
        /// <param name="radius"></param>
        /// <returns></returns>
        private Point GetCircumferencePoint(Double angle, Double radius)
        {
            Double angle_radian = (angle*Math.PI)/180;
            //Radius-- is the Radius of the gauge
            Double X = ((Radius) + (radius)*Math.Cos(angle_radian));
            Double Y = ((Radius) + (radius)*Math.Sin(angle_radian));
            var p = new Point(X, Y);
            return p;
        }

        /// <summary>
        /// Draw the range indicator
        /// </summary>
        private void DrawRangeIndicator()
        {
            Double realworldunit = (ScaleSweepAngle/(MaxValue - MinValue));
            Double optimalStartAngle;
            Double optimalEndAngle;
            double db;

            //Checking whether the  OptimalRangeStartvalue is -ve 
            if (OptimalRangeStartValue < 0)
            {
                db = MinValue + Math.Abs(OptimalRangeStartValue);
                optimalStartAngle = ((Math.Abs(db*realworldunit)));
            }
            else
            {
                db = Math.Abs(MinValue) + OptimalRangeStartValue;
                optimalStartAngle = ((db*realworldunit));
            }

            //Checking whether the  OptimalRangeEndvalue is -ve
            if (OptimalRangeEndValue < 0)
            {
                db = MinValue + Math.Abs(OptimalRangeEndValue);
                optimalEndAngle = ((Math.Abs(db*realworldunit)));
            }
            else
            {
                db = Math.Abs(MinValue) + OptimalRangeEndValue;
                optimalEndAngle = ((db*realworldunit));
            }
            // calculating the angle for optimal Start value

            Double optimalStartAngleFromStart = (ScaleStartAngle + optimalStartAngle);

            // calculating the angle for optimal Start value

            Double optimalEndAngleFromStart = (ScaleStartAngle + optimalEndAngle);

            //Calculating the Radius of the two arc for segment 
            arcradius1 = (RangeIndicatorRadius + RangeIndicatorThickness);
            arcradius2 = RangeIndicatorRadius;

            double endAngle = ScaleStartAngle + ScaleSweepAngle;

            // Calculating the Points for the below Optimal Range segment from the center of the gauge

            Point A = GetCircumferencePoint(ScaleStartAngle, arcradius1);
            Point B = GetCircumferencePoint(ScaleStartAngle, arcradius2);
            Point C = GetCircumferencePoint(optimalStartAngleFromStart, arcradius2);
            Point D = GetCircumferencePoint(optimalStartAngleFromStart, arcradius1);

            bool isReflexAngle = Math.Abs(optimalStartAngleFromStart - ScaleStartAngle) > 180.0;
            DrawSegment(A, B, C, D, isReflexAngle, BelowOptimalRangeColor);

            // Calculating the Points for the Optimal Range segment from the center of the gauge

            Point A1 = GetCircumferencePoint(optimalStartAngleFromStart, arcradius1);
            Point B1 = GetCircumferencePoint(optimalStartAngleFromStart, arcradius2);
            Point C1 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius2);
            Point D1 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius1);
            bool isReflexAngle1 = Math.Abs(optimalEndAngleFromStart - optimalStartAngleFromStart) > 180.0;
            DrawSegment(A1, B1, C1, D1, isReflexAngle1, OptimalRangeColor);

            // Calculating the Points for the Above Optimal Range segment from the center of the gauge

            Point A2 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius1);
            Point B2 = GetCircumferencePoint(optimalEndAngleFromStart, arcradius2);
            Point C2 = GetCircumferencePoint(endAngle, arcradius2);
            Point D2 = GetCircumferencePoint(endAngle, arcradius1);
            bool isReflexAngle2 = Math.Abs(endAngle - optimalEndAngleFromStart) > 180.0;
            DrawSegment(A2, B2, C2, D2, isReflexAngle2, AboveOptimalRangeColor);
        }

        //Drawing the segment with two arc and two line

        private void DrawSegment(Point p1, Point p2, Point p3, Point p4, bool reflexangle, Color clr)
        {
            // Segment Geometry
            var segments = new PathSegmentCollection
                               {
                                   new LineSegment {Point = p2}, // First line segment from pt p1 - pt p2
                                   new ArcSegment
                                       {
                                           Size = new Size(arcradius2, arcradius2),
                                           Point = p3,
                                           SweepDirection = SweepDirection.Clockwise,
                                           IsLargeArc = reflexangle
                                       }, //Arc drawn from pt p2 - pt p3 with the RangeIndicatorRadius
                                   new LineSegment {Point = p4}, // Second line segment from pt p3 - pt p4
                                   new ArcSegment
                                       {
                                           Size = new Size(arcradius1, arcradius1),
                                           Point = p1,
                                           SweepDirection = SweepDirection.Counterclockwise,
                                           IsLargeArc = reflexangle
                                       } //Arc drawn from pt p4 - pt p1 with the Radius of arcradius1 
                               };

            // Defining the segment path properties
            Color rangestrokecolor = clr == Colors.Transparent ? clr : Colors.White;

            rangeIndicator = new Path {StrokeLineJoin = PenLineJoin.Round, Stroke = new SolidColorBrush(rangestrokecolor), //Color.FromArgb(0xFF, 0xF5, 0x9A, 0x86)
                                       Fill = new SolidColorBrush(clr), Opacity = 0.65, StrokeThickness = 0.25, Data = new PathGeometry {Figures = new PathFigureCollection {new PathFigure {IsClosed = true, StartPoint = p1, Segments = segments}}}};

            //Set Z index of range indicator
            rangeIndicator.SetValue(Canvas.ZIndexProperty, 150);
            // Adding the segment to the root grid 
            rootGrid.Children.Add(rangeIndicator);
        }

        #endregion
    }
}